package com.fingard.dsp.bank.directbank.nnbill01;

import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.*;

public class Crypto {
    public static final String KEY_ALGORITHM = "RSA";
    public static final String SHA1_ALGORITHM = "SHA-1";
    static Base64 base64 = new Base64();
    public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
    public static sun.misc.BASE64Decoder decoder = new sun.misc.BASE64Decoder();
    public static sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();

    public static String buildSortWaitSign(Map<String, String> mapPars, boolean ExcludeBlankValue) {
        StringBuilder sbPars = new StringBuilder();
        List<String> lsPars = new ArrayList<String>();
        for (Iterator i = mapPars.keySet().iterator(); i.hasNext(); ) {
            String strKey = String.valueOf(i.next());
            lsPars.add(strKey);
        }
        String[] tmpPars = (String[]) lsPars.toArray(new String[lsPars.size()]);
        Arrays.sort(tmpPars);
        for (Integer i = 0; i < tmpPars.length; i++) {
            String strKey = tmpPars[i];
            String strValue = String.valueOf(mapPars.get(strKey));
            if (null == strValue || strValue.length() == 0) {
                if (ExcludeBlankValue) {
                    strValue = "";
                } else {
                    continue;
                }
            }
            if (i == tmpPars.length - 1) {
                sbPars.append(String.format("%s=%s", strKey, strValue));
            } else {
                sbPars.append(String.format("%s=%s&", strKey, strValue));
            }
        }
        return sbPars.toString();
    }

    public static String buildObjectSortWaitSign(Map<String, Object> mapPars, boolean ExcludeBlankValue) {
        StringBuilder sbPars = new StringBuilder();
        List<String> lsPars = new ArrayList<String>();
        for (Iterator i = mapPars.keySet().iterator(); i.hasNext(); ) {
            String strKey = String.valueOf(i.next());
            lsPars.add(strKey);
        }
        String[] tmpPars = (String[]) lsPars.toArray(new String[lsPars.size()]);
        Arrays.sort(tmpPars);
        for (Integer i = 0; i < tmpPars.length; i++) {
            String strKey = tmpPars[i];
            String strValue = String.valueOf(mapPars.get(strKey));
            if (null == strValue || strValue.length() == 0) {
                if (ExcludeBlankValue) {
                    strValue = "";
                } else {
                    continue;
                }
            }
            if (i == tmpPars.length - 1) {
                sbPars.append(String.format("%s=%s", strKey, strValue));
            } else {
                sbPars.append(String.format("%s=%s&", strKey, strValue));
            }
        }
        return sbPars.toString();
    }
    /**
     * SHA1签名
     * @param maps
     * @param key
     * @return
     */
    /*public static String sign(Map<String,String> maps, String key) { 
        //获取信息摘要 - 参数字典排序后字符串  
        String decrypt = buildSortWaitSign(maps, false);
        decrypt += "&key=" + key;
        //指定sha1算法  
        MessageDigest digest;
		try {
			digest = MessageDigest.getInstance(SHA1_ALGORITHM);
			digest.update(decrypt.getBytes());  
            //获取字节数组  
			byte[] messageDigest = digest.digest(); 
			return bytesToHexString(messageDigest);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}  
         
        return "";
	}*/

    /**
     * 验签
     * @param maps：返回参数
     * @param key：密钥串
     * @param serverSign：验签串
     * @return：验签是否通过
     */
	/*public static boolean verify(Map<String,String> maps, String key, String serverSign){
		String strSign = sign(maps,key);
		if(strSign.equalsIgnoreCase(serverSign)){
			return true;
		}
		return false;
	}*/

    /**
     * SHA-1签名
     *
     * @param maps
     * @param key
     * @param logger
     * @return
     */
    public static String signObject(Map<String, Object> maps, String key, Logger logger) {
        //获取信息摘要 - 参数字典排序后字符串  
        String decrypt = buildObjectSortWaitSign(maps, false);
        decrypt += "&key=" + key;
        //指定sha1算法  

        StringBuilder sbErr = new StringBuilder();
        try {
            //获取字节数组  
            byte[] messageDigest = sign(SHA1_ALGORITHM, decrypt, sbErr);
            return bytesToHexString(messageDigest);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return "";
    }

    public static byte[] sign(String algorithm, String waitSign, StringBuilder sbErr) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance(SHA1_ALGORITHM);
            digest.update(waitSign.getBytes("UTF-8"));
            //获取字节数组  
            return digest.digest();
        } catch (NoSuchAlgorithmException e) {
            sbErr.append(String.format("发生异常：%s", e));
            e.printStackTrace();
        } catch (Exception ex) {
            sbErr.append(String.format("异常：%s", ex));
        }
        return null;
    }

    /**
     * SHA-1验签
     *
     * @param maps
     * @param key：密钥
     * @param serverSign：验签串
     * @param logger
     * @return
     */
    public static boolean verifyObject(Map<String, Object> maps, String key, String serverSign, Logger logger) {
        String strSign = signObject(maps, key, logger);
        logger.debug(String.format("签名结果：%s验签串：%s", strSign, serverSign));
        if (strSign.equalsIgnoreCase(serverSign)) {
            return true;
        }
        return false;
    }

    public static String encrypt(byte[] data, String privateKey) throws Exception {
        byte[] dec = decoder.decodeBuffer(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(dec);

        // KEY_ALGORITHM 指定的加密算法  
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);

        // 取私钥匙对象  
        PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);

        // 用私钥对信息生成数字签名  
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(priKey);
        signature.update(data);

        return encoder.encode(signature.sign());
    }

    public static boolean hasAnyChar(String p_str) {
        if (p_str != null && p_str.length() > 0) {
            return true;
        }
        return false;
    }

    public static byte[] checkPEM(byte[] paramArrayOfByte) {
        String str1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/+= \r\n-";
        for (int i = 0; i < paramArrayOfByte.length; i++)
            if (str1.indexOf(paramArrayOfByte[i]) == -1)
                return null;
        StringBuffer localStringBuffer = new StringBuffer(
                paramArrayOfByte.length);
        String str2 = new String(paramArrayOfByte);
        for (int j = 0; j < str2.length(); j++)
            if ((str2.charAt(j) != ' ') && (str2.charAt(j) != '\r') &&
                    (str2.charAt(j) != '\n'))
                localStringBuffer.append(str2.charAt(j));
        return localStringBuffer.toString().getBytes();
    }

    public static byte[] hexToByte(byte[] b) {
        if (b.length % 2 != 0) {
            throw new IllegalArgumentException("输入长度不是偶数");
        }
        byte[] b2 = new byte[b.length / 2];
        for (int n = 0; n < b.length; n += 2) {
            String item = new String(b, n, 2);

            b2[(n / 2)] = ((byte) Integer.parseInt(item, 16));
        }
        b = (byte[]) null;
        return b2;
    }

    /**
     * 将字节转成十六进制字符串
     *
     * @param p_bytes
     * @return
     */
    public static String bytesToHexString(byte[] p_bytes) {
        if (p_bytes == null || p_bytes.length <= 0) {
            return null;
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < p_bytes.length; i++) {
            int each = p_bytes[i] & 0xFF;
            String eachHex = Integer.toHexString(each);
            if (eachHex.length() < 2) {
                sb.append(0);
            }
            sb.append(eachHex);
        }
        return sb.toString();
    }

    private char[] getChars(byte[] bytes, String encoding) {
        Charset cs = Charset.forName(encoding);
        ByteBuffer bb = ByteBuffer.allocate(bytes.length);
        bb.put(bytes);
        bb.flip();
        CharBuffer cb = cs.decode(bb);
        return cb.array();
    }

    /**
     * RSA签名
     *
     * @param p_toSign
     * @param p_ksType
     * @param p_algorithm
     * @param p_ksPath
     * @param p_kPass
     * @param p_sbLog
     * @return
     */
    public static byte[] signData(byte[] p_toSign, String p_ksType, String p_algorithm, String p_ksPath, String p_kPass, StringBuilder p_sbLog) throws IOException {
        FileInputStream fis = null;
        try {
            KeyStore ks = KeyStore.getInstance(p_ksType);//"PKCS12"
            fis = new FileInputStream(p_ksPath);
            char[] keyPassword = null;
            if (hasAnyChar(p_kPass)) {
                keyPassword = p_kPass.toCharArray();
            }
            ks.load(fis, keyPassword);
            fis.close();
            fis = null;

            @SuppressWarnings("rawtypes")
            Enumeration enumAlias = ks.aliases();
            String keyAlias = null;
            if (enumAlias.hasMoreElements()) {
                keyAlias = (String) enumAlias.nextElement();
            }
            PrivateKey priKey = (PrivateKey) ks.getKey(keyAlias, keyPassword);

            Signature signProvider = Signature.getInstance(p_algorithm);//"SHA1withRSA"
            signProvider.initSign(priKey);
            signProvider.update(p_toSign);
            return signProvider.sign();
        } catch (Exception ex) {
            p_sbLog.append(ex.getMessage());
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
        return null;
    }

    public static String getPublicKey(String p_ksType, String p_ksPath, String p_kPass, StringBuilder p_sbLog) throws IOException {
        FileInputStream fis = null;
        try {
            KeyStore ks = KeyStore.getInstance(p_ksType);//"PKCS12"
            fis = new FileInputStream(p_ksPath);
            char[] keyPassword = null;
            if (hasAnyChar(p_kPass)) {
                keyPassword = p_kPass.toCharArray();
            }
            ks.load(fis, keyPassword);
            fis.close();
            fis = null;
            @SuppressWarnings("rawtypes")
            Enumeration enumAlias = ks.aliases();
            String keyAlias = null;
            if (enumAlias.hasMoreElements()) {
                keyAlias = (String) enumAlias.nextElement();
            }
            Certificate cer = ks.getCertificate(keyAlias);
            PublicKey pk = cer.getPublicKey();
            return encoder.encode(pk.getEncoded());
        } catch (Exception ex) {
            p_sbLog.append(ex.getMessage());
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
        return "";
    }

    public static byte[] getBytePublicKey(String p_ksType, String p_ksPath, String p_kPass, StringBuilder p_sbLog) throws IOException {
        FileInputStream fis = null;
        try {
            KeyStore ks = KeyStore.getInstance(p_ksType);//"PKCS12"
                    fis = new FileInputStream(p_ksPath);
            char[] keyPassword = null;
            if (hasAnyChar(p_kPass)) {
                keyPassword = p_kPass.toCharArray();
            }
            ks.load(fis, keyPassword);
            fis.close();
            fis = null;
            @SuppressWarnings("rawtypes")
            Enumeration enumAlias = ks.aliases();
            String keyAlias = null;
            if (enumAlias.hasMoreElements()) {
                keyAlias = (String) enumAlias.nextElement();
            }
            Certificate cer = ks.getCertificate(keyAlias);
            return cer.getEncoded();
        } catch (Exception ex) {
            p_sbLog.append(ex.getMessage());
        } finally {
            if (fis != null) {
                fis.close();
            }
        }
        return null;
    }

    /**
     * MD5验签
     *
     * @param waitSign
     * @param checkValue
     * @param encoding
     * @param sbErr
     * @return
     */
    public static boolean verifyMD5(String waitSign, String checkValue, String encoding, StringBuilder sbErr) {
        String tmpSign = md5ToHexString(waitSign, encoding, sbErr);
        if (checkValue.equalsIgnoreCase(tmpSign)) {
            return true;
        }
        return false;
    }

    /**
     * 生成MD5摘要及转成十六进制字符串
     *
     * @param waitSign
     * @param encoding
     * @param sbErr
     * @return
     */
    public static String md5ToHexString(String waitSign, String encoding, StringBuilder sbErr) {
        return bytesToHexString(md5(waitSign, encoding, sbErr));
    }

    /**
     * 生成MD5摘要
     *
     * @param waitSign：待签串
     * @param encoding：字符集
     * @param sbErr：错误信息
     * @return
     */
    public static byte[] md5(String waitSign, String encoding, StringBuilder sbErr) {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("MD5");
            digest.update(waitSign.getBytes(encoding));
            //获取字节数组  
            return digest.digest();
        } catch (NoSuchAlgorithmException e) {
            sbErr.append(String.format("发生异常：%s", e));
            e.printStackTrace();
        } catch (Exception ex) {
            sbErr.append(String.format("异常：%s", ex));
        }
        return null;
    }

    /**
     * 从公钥文件中获取公钥信息
     *
     * @param path：公钥文件路径
     * @param sbErr：错误信息
     * @return
     */
    public static PublicKey getPublicKey(String path, StringBuilder sbErr) {
        CertificateFactory cf = null;
        FileInputStream in = null;
        try {
            cf = CertificateFactory.getInstance("X.509");
            in = new FileInputStream(path);
            X509Certificate verifyCert = ((X509Certificate) cf.generateCertificate(in));
            PublicKey pubKey = verifyCert.getPublicKey();
            return pubKey;
        } catch (Exception ex) {
            sbErr.append(String.format("获取公钥异常：%s，信息：%s", ex.getMessage(), ex.getStackTrace()));
        }
        return null;
    }

    public static X509Certificate getCertFromHexString(String hexCert) throws SecurityException {
        ByteArrayInputStream bIn = null;
        X509Certificate certobj = null;
        try {
            byte[] cert = hexToByte(hexCert.getBytes());
            CertificateFactory fact = CertificateFactory.getInstance("X.509");
            bIn = new ByteArrayInputStream(cert);
            certobj = (X509Certificate) fact.generateCertificate(bIn);
            bIn.close();
            bIn = null;
        } catch (CertificateException e) {
            e.printStackTrace();
            try {
                if (bIn != null)
                    bIn.close();
            } catch (IOException localIOException1) {
            }
        } catch (IOException e) {
            e.printStackTrace();
            try {
                if (bIn != null)
                    bIn.close();
            } catch (IOException localIOException2) {
            }
        } finally {
            try {
                if (bIn != null)
                    bIn.close();
            } catch (IOException localIOException3) {
            }
        }
        return certobj;
    }

    public static byte[] sign(String signtype, String strParameter, String encoding, String keytype, String algorithm, String priKey, String priKeypass, StringBuilder sbErr) {
        byte[] tmpSign = null;
        try {
            if ("MD5".equals(signtype)) {
                tmpSign = md5(strParameter, encoding, sbErr);
            } else if ("RSA".equals(signtype)) {
                tmpSign = Crypto.signData(strParameter.getBytes(encoding), keytype, algorithm, priKey, priKeypass, sbErr);
            }
        } catch (Exception ex) {
            sbErr.append(ex.getMessage());
        }
        return tmpSign;
    }

    /**
     * 验签
     *
     * @param returnInfoByte：待签串字节数组
     * @param serverSignByte：待验签串字节数组
     * @param serverCert：公钥路径
     * @param algorithm：算法
     * @param encoding：编码
     * @param sbErr：错误消息
     * @return
     */
    public static boolean verify(byte[] returnInfoByte, byte[] serverSignByte, String serverCert, String algorithm, String encoding, StringBuilder sbErr) {
        try {
            PublicKey publicKey = Crypto.getPublicKey(serverCert, sbErr);
            Signature signet = Signature.getInstance(algorithm);
            signet.initVerify(publicKey);
            signet.update(returnInfoByte);
            return signet.verify(serverSignByte);
        } catch (NoSuchAlgorithmException e) {
            sbErr.append(e.getMessage());
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            sbErr.append(e.getMessage());
            e.printStackTrace();
        } catch (SignatureException e) {
            sbErr.append(e.getMessage());
            e.printStackTrace();
        }
        return false;
    }
}

